//
// Created by chaseaishang on 23-5-16.
//

#include "Portocol.h"
#include "Gamedata.h"
#include <event.h>
using namespace std;
void Portocol::init()
{


    m_fd = socket(AF_INET, SOCK_STREAM, 0);
    cout<<"fd= "<<m_fd<<endl;
    if(-1 == m_fd){
        cout<<"Error, connectServer() quit"<<endl;
        ::exit(-1);
    }
    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_port = htons(9876);
    inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
    int con_result = connect(m_fd, (struct sockaddr*) &serv, sizeof(struct sockaddr));
    if(con_result==-1)
    {
        cout<<"failed";
    }
    int succeed=pthread_mutex_init(&this->m_mutex, nullptr);
    succeed=pthread_mutex_init(&m_Write_mutex, nullptr);
    this->init_read_event_thread();

}






bool Portocol::sendToServer(char *data, int len,game_state state)
{
    cout<<"sendBegin\n";
    while(could_read)
    {
        continue;
    }
    pthread_mutex_lock(&m_mutex);



    int write_num = write(m_fd, data, len+1);
    could_read= true;
    pthread_mutex_unlock(&m_mutex);
    cout<<"sendEnd\n";
    return write_num==len+1;
}
game_state Portocol::recvFromServerNoBlocking(char *, int &len, GameData::In_gameData *gamedata) {
    if(gamedata== nullptr)
    {
        this->ready= true;
    }



    return this->m_state;
}
game_state Portocol::recvFromServer(char*message,int &len,GameData::In_gameData *gamedata)
{
    cout<<"recvBegin\n";
    while(!could_write)
    {
        continue;
    }
    pthread_mutex_lock(&m_Write_mutex);

    strcpy(message,m_message);
    could_write= false;
    pthread_mutex_unlock(&m_Write_mutex);
    len= m_message[0];
    if(gamedata== nullptr)
    {
        this->ready= true;
        auto state=GameData::unpackData(m_message,len, nullptr);
        return state;
    }
    else
    {
        auto state=GameData::unpackData(m_message,len, gamedata);
        return state;
    }




}

Portocol::~Portocol() {

    pthread_mutex_destroy(&this->m_mutex);
    pthread_mutex_destroy(&this->m_Write_mutex);

}

void Portocol::unpackData(char *buf, int &len)
{
    game_state state;
    if(this->ready)
    {
        state=GameData::unpackData(buf,len, nullptr);
    }
    else
    {
        state=GameData::unpackData(buf,len,&this->gamedata);
    }

    m_state=state;
    cout<<"Portocol::unpackData";

}

void Portocol::on_read(int sock, short event, void *arg)
{
    Portocol*p_this=(Portocol*)arg;
    char buffer[100];
    memset(buffer, 0, sizeof(char)*100);
    //--本来应该用while一直循环，但由于用了libevent，只在可以读的时候才触发on_read(),故不必用while了
    char len=0;
    while(!p_this->could_read)
    {
        continue;
    };
    pthread_mutex_lock(&p_this->m_mutex);
    while(p_this->could_write)
    {
        continue;
    }
    pthread_mutex_lock(&p_this->m_Write_mutex);

    int size = read(sock, &len, 1);
    if(0 == size)
    {//说明socket关闭
        cout<<"read size is 0 for socket:"<<sock<<endl;
        struct event* read_ev = p_this->read_ev;
        if(NULL != read_ev)
        {
            event_del(read_ev);
            free(read_ev);
        }
        close(sock);
        return;
    }
    if(-1==size)
    {
        cout<<"other error!";
    }
    buffer[0]=len;
    size = read(sock, buffer+1, len);



    memcpy(p_this->m_message,buffer,len+1);
    p_this->could_write= true;
    cout<<"readnumber="<<size<<endl;

    pthread_mutex_unlock(&p_this->m_Write_mutex);
    p_this->could_read= false;
    pthread_mutex_unlock(&p_this->m_mutex);
    cout<<"Received from server:   "<<buffer+1<<endl;



}

GameData::In_gameData Portocol::GetgameData() {


    return gamedata;
}
void* Portocol::init_read_event(void* arg){
    Portocol*p_this=(Portocol*)arg;
    long long_sock = p_this->m_fd;
    int sock = (int)long_sock;
    //-----初始化libevent，设置回调函数on_read()------------
    struct event_base* base = event_base_new();
    struct event* read_ev = (struct event*)malloc(sizeof(struct event));//发生读事件后，从socket中取出数据
    p_this->read_ev=read_ev;
    event_set(read_ev, sock, EV_READ|EV_PERSIST, Portocol::on_read, arg);
    event_base_set(base, read_ev);
    event_add(read_ev, NULL);
    event_base_dispatch(base);
    //--------------
    event_base_free(base);
    cout<<"out_event_base\n";
}
void Portocol::init_read_event_thread(){
    pthread_t thread;
    pthread_create(&thread,NULL,Portocol::init_read_event,(void*)this);
    pthread_detach(thread);
}


